To be able to edit code and run cells, you need to run the notebook yourself. Where would you like to run the notebook?

In the cloud (experimental)

Binder is a free, open source service that runs scientific notebooks in the cloud! It will take a while, usually 2-7 minutes to get a session.

On your computer

(Recommended if you want to store your changes.)

  1. Copy the notebook URL:
  2. Run Pluto

    (Also see: How to install Julia and Pluto)

  3. Paste URL in the Open box

Frontmatter

If you are publishing this notebook on the web, you can set the parameters below to provide HTML metadata. This is useful for search engines and social media.

Author 1

homework 1, version 0

👀 Reading hidden code
21.7 ms

Submission by: SOLUTIONS (SOLUTIONS@mit.edu)

👀 Reading hidden code
15.3 ms
👀 Reading hidden code
html"""
<style>
pluto-output p {
font-family: cursive;
color: green;
}
</style>

"""
119 μs

Homework 1 - convolutions

18.S191, fall 2020

This notebook contains built-in, live answer checks! In some exercises you will see a coloured box, which runs a test case on your code, and provides feedback based on the result. Simply edit the code, run it, and the check runs again.

For MIT students: there will also be some additional (secret) test cases that will be run as part of the grading process, and we will look at your notebook and write comments.

Feel free to ask questions!

👀 Reading hidden code
37.6 ms
👀 Reading hidden code
# edit the code below to set your name and kerberos ID (i.e. email without @mit.edu)

student = (name = "SOLUTIONS", kerberos_id = "SOLUTIONS")

# press the ▶ button in the bottom right of this cell to run your edits
# or use Shift+Enter

# you might need to wait until all other cells in this notebook have completed running.
# scroll down the page to see what's up
18.9 μs

Let's create a package environment:

👀 Reading hidden code
319 μs
begin
import Pkg
Pkg.activate(mktempdir())
end
👀 Reading hidden code
❔
  Activating new project at `/tmp/jl_PBYz5h`
142 ms

We set up Images.jl again:

👀 Reading hidden code
259 μs
begin
Pkg.add(["Images", "ImageIO", "ImageMagick"])
using Images
end
👀 Reading hidden code
❔
    Updating registry at `~/.julia/registries/General.toml`
   Resolving package versions...
    Updating `/tmp/jl_PBYz5h/Project.toml`
  [82e4d734] + ImageIO v0.6.9
  [6218d12a] + ImageMagick v1.4.0
  [916415d5] + Images v0.26.2
    Updating `/tmp/jl_PBYz5h/Manifest.toml`
  [621f4979] + AbstractFFTs v1.5.0
  [79e6a3ab] + Adapt v3.7.2
  [66dad0bd] + AliasTables v1.1.3
  [ec485272] + ArnoldiMethod v0.4.0
  [4fba245c] + ArrayInterface v7.5.1
  [13072b0f] + AxisAlgorithms v1.1.0
  [39de3d68] + AxisArrays v0.4.7
  [62783981] + BitTwiddlingConvenienceFunctions v0.1.6
  [fa961155] + CEnum v0.5.0
  [2a0fbf3d] + CPUSummary v0.2.6
  [aafaddc9] + CatIndices v0.2.2
  [d360d2e6] + ChainRulesCore v1.25.1
  [9e997f8a] + ChangesOfVariables v0.1.9
  [fb6a15b2] + CloseOpenIntervals v0.1.13
  [aaaa29a8] + Clustering v0.15.8
  [35d6a980] + ColorSchemes v3.29.0
  [3da002f7] + ColorTypes v0.12.0
  [c3611d14] + ColorVectorSpace v0.11.0
  [5ae59095] + Colors v0.13.0
  [bbf7d656] + CommonSubexpressions v0.3.1
  [34da2185] + Compat v4.16.0
  [ed09eef8] + ComputationalResources v0.3.2
  [187b0558] + ConstructionBase v1.5.8
  [150eb455] + CoordinateTransformations v0.6.3
  [adafc99b] + CpuId v0.3.1
  [dc8bdbbb] + CustomUnitRanges v1.0.2
  [9a962f9c] + DataAPI v1.16.0
  [864edb3b] + DataStructures v0.18.20
  [163ba53b] + DiffResults v1.1.0
  [b552c78f] + DiffRules v1.15.1
  [b4f34e82] + Distances v0.10.12
  [ffbed154] + DocStringExtensions v0.9.3
  [4f61f5a4] + FFTViews v0.3.2
  [7a1cc6ca] + FFTW v1.8.1
  [5789e2e9] + FileIO v1.17.0
  [53c48c17] + FixedPointNumbers v0.8.5
  [f6369f11] + ForwardDiff v0.10.38
  [a2bd30eb] + Graphics v1.1.3
  [86223c79] + Graphs v1.12.0
  [2c695a8d] + HistogramThresholding v0.3.1
  [3e5b6fbb] + HostCPUFeatures v0.1.17
  [615f187c] + IfElse v0.1.1
  [2803e5a7] + ImageAxes v0.6.12
  [c817782e] + ImageBase v0.1.7
  [cbc4b850] + ImageBinarization v0.3.1
  [f332f351] + ImageContrastAdjustment v0.3.12
  [a09fc81d] + ImageCore v0.10.5
  [89d5987c] + ImageCorners v0.1.3
  [51556ac3] + ImageDistances v0.2.17
  [6a3955dd] + ImageFiltering v0.7.9
  [82e4d734] + ImageIO v0.6.9
  [6218d12a] + ImageMagick v1.4.0
  [bc367c6b] + ImageMetadata v0.9.10
  [787d08f9] + ImageMorphology v0.4.5
  [2996bd0c] + ImageQualityIndexes v0.3.7
  [80713f31] + ImageSegmentation v1.8.4
  [4e3cecfd] + ImageShow v0.3.8
  [02fcd773] + ImageTransformations v0.10.1
  [916415d5] + Images v0.26.2
  [9b13fd28] + IndirectArrays v1.0.0
  [d25df0c9] + Inflate v0.1.5
  [1d092043] + IntegralArrays v0.1.6
  [a98d9a8b] + Interpolations v0.15.1
  [8197267c] + IntervalSets v0.7.10
  [3587e190] + InverseFunctions v0.1.17
  [92d709cd] + IrrationalConstants v0.2.4
  [c8e1da08] + IterTools v1.4.0
  [033835bb] + JLD2 v0.5.11
  [692b3bcd] + JLLWrappers v1.7.0
  [b835a17e] + JpegTurbo v0.1.5
  [10f19ff3] + LayoutPointers v0.1.17
  [8cdb02fc] + LazyModules v0.3.1
  [2ab3a3ac] + LogExpFunctions v0.3.28
  [bdcacae8] + LoopVectorization v0.12.171
  [1914dd2f] + MacroTools v0.5.15
  [d125e4d3] + ManualMemory v0.1.8
  [dbb5928d] + MappedArrays v0.4.2
  [626554b9] + MetaGraphs v0.8.0
  [e1d29d7a] + Missings v1.2.0
  [e94cdb99] + MosaicViews v0.3.4
  [77ba4419] + NaNMath v1.0.3
  [b8a86587] + NearestNeighbors v0.4.21
  [f09324ee] + Netpbm v1.1.1
  [6fe1bfb0] + OffsetArrays v1.15.0
  [52e1d378] + OpenEXR v0.3.3
  [bac558e1] + OrderedCollections v1.8.0
  [f57f5aa1] + PNGFiles v0.4.4
  [5432bcbf] + PaddedViews v0.5.12
  [d96e819e] + Parameters v0.12.3
  [eebad327] + PkgVersion v0.3.3
  [1d0040c9] + PolyesterWeave v0.2.2
  [f27b6e38] + Polynomials v4.0.19
  [aea7be01] + PrecompileTools v1.2.1
  [21216c6a] + Preferences v1.4.3
  [92933f4c] + ProgressMeter v1.10.2
  [43287f4e] + PtrArrays v1.3.0
  [4b34888f] + QOI v1.0.1
  [94ee1d12] + Quaternions v0.7.6
  [b3c3ace0] + RangeArrays v0.3.2
  [c84ed2f1] + Ratios v0.4.5
  [c1ae055f] + RealDot v0.1.0
  [3cdcf5f2] + RecipesBase v1.3.4
  [189a3867] + Reexport v1.2.2
  [dee08c22] + RegionTrees v0.3.2
  [ae029012] + Requires v1.3.1
  [6038ab10] + Rotations v1.7.1
  [fdea26ae] + SIMD v3.7.1
  [94e857df] + SIMDTypes v0.1.0
  [476501e8] + SLEEFPirates v0.6.43
  [efcf1570] + Setfield v1.1.2
  [699a6c99] + SimpleTraits v0.9.4
  [47aef6b3] + SimpleWeightedGraphs v1.4.0
  [45858cf5] + Sixel v0.1.3
  [a2af1166] + SortingAlgorithms v1.2.1
  [276daf66] + SpecialFunctions v2.5.0
  [cae243ae] + StackViews v0.1.1
  [aedffcd0] + Static v0.8.9
  [0d7ed370] + StaticArrayInterface v1.6.0
  [90137ffa] + StaticArrays v1.9.13
  [1e83bf80] + StaticArraysCore v1.4.3
  [82ae8749] + StatsAPI v1.7.0
  [2913bbd2] + StatsBase v0.34.4
  [62fd8b95] + TensorCore v0.1.1
  [8290d209] + ThreadingUtilities v0.5.2
  [731e570b] + TiffImages v0.11.3
  [06e1c1a7] + TiledIteration v0.5.0
  [3bb67fe8] + TranscodingStreams v0.11.3
  [3a884ed6] + UnPack v1.0.2
  [3d5dd08c] + VectorizationBase v0.21.71
  [e3aaa7dc] + WebP v0.1.3
  [efce3f68] + WoodburyMatrices v1.0.0
  [f5851436] + FFTW_jll v3.3.10+3
  [61579ee1] + Ghostscript_jll v9.55.0+4
  [59f7168a] + Giflib_jll v5.2.3+0
  [c73af94c] + ImageMagick_jll v7.1.1+1
  [905a6f67] + Imath_jll v3.1.11+0
  [1d5cc7b8] + IntelOpenMP_jll v2025.0.4+0
  [aacddb02] + JpegTurbo_jll v3.1.1+0
  [88015f11] + LERC_jll v3.0.0+1
  [d4300ac3] + Libgcrypt_jll v1.11.0+0
  [7e76a0d4] + Libglvnd_jll v1.7.0+0
  [7add5ba3] + Libgpg_error_jll v1.51.1+0
  [94ce4f54] + Libiconv_jll v1.18.0+0
  [89763e89] + Libtiff_jll v4.4.0+0
  [d3a379c0] + LittleCMS_jll v2.12.0+0
  [856f044c] + MKL_jll v2025.0.1+1
  [18a262bb] + OpenEXR_jll v3.2.4+0
  [643b3616] + OpenJpeg_jll v2.4.0+0
  [efe28fd5] + OpenSpecFun_jll v0.5.6+0
  [02c8fc9c] + XML2_jll v2.13.6+1
  [aed1982a] + XSLT_jll v1.1.42+0
  [4f6342f7] + Xorg_libX11_jll v1.8.6+3
  [0c0b7dd1] + Xorg_libXau_jll v1.0.12+0
  [a3789734] + Xorg_libXdmcp_jll v1.1.5+0
  [1082639a] + Xorg_libXext_jll v1.3.6+3
  [14d82f49] + Xorg_libpthread_stubs_jll v0.1.2+0
  [c7cfdc94] + Xorg_libxcb_jll v1.17.0+3
  [c5fb5394] + Xorg_xtrans_jll v1.5.1+0
  [3161d3a3] + Zstd_jll v1.5.7+1
  [b53b4c65] + libpng_jll v1.6.47+0
  [075b6546] + libsixel_jll v1.10.5+0
  [c5f90fcd] + libwebp_jll v1.4.0+0
  [1317d2d5] + oneTBB_jll v2022.0.0+0
  [0dad84c5] + ArgTools
  [56f22d72] + Artifacts
  [2a0f44e3] + Base64
  [ade2ca70] + Dates
  [8ba89e20] + Distributed
  [f43a241f] + Downloads
  [7b1f6079] + FileWatching
  [9fa8497b] + Future
  [b77e0a4c] + InteractiveUtils
  [4af54fe1] + LazyArtifacts
  [b27032c2] + LibCURL
  [76f85450] + LibGit2
  [8f399da3] + Libdl
  [37e2e46d] + LinearAlgebra
  [56ddb016] + Logging
  [d6f4376e] + Markdown
  [a63ad114] + Mmap
  [ca575930] + NetworkOptions
  [44cfe95a] + Pkg
  [de0858da] + Printf
  [3fa0cd96] + REPL
  [9a3f8284] + Random
  [ea8e919c] + SHA
  [9e88b42a] + Serialization
  [1a1011a3] + SharedArrays
  [6462fe0b] + Sockets
  [2f01184e] + SparseArrays
  [10745b16] + Statistics
  [4607b0f0] + SuiteSparse
  [fa267f1f] + TOML
  [a4e569a6] + Tar
  [8dfed614] + Test
  [cf7118a7] + UUIDs
  [4ec0a83e] + Unicode
  [e66e0078] + CompilerSupportLibraries_jll
  [deac9b47] + LibCURL_jll
  [29816b5a] + LibSSH2_jll
  [c8ffd9c3] + MbedTLS_jll
  [14a3606d] + MozillaCACerts_jll
  [4536629a] + OpenBLAS_jll
  [05823500] + OpenLibm_jll
  [83775a58] + Zlib_jll
  [8e850b90] + libblastrampoline_jll
  [8e850ede] + nghttp2_jll
  [3f19e933] + p7zip_jll
11.1 s





👀 Reading hidden code
11.3 μs

Exercise 1 - Manipulating vectors (1D images)

A Vector is a 1D array. We can think of that as a 1D image.

👀 Reading hidden code
6.5 ms
👀 Reading hidden code
17.5 μs
colored_line(example_vector)
👀 Reading hidden code
15.1 μs

Exerise 1.1

👉 Make a random vector random_vect of length 10 using the rand function.

👀 Reading hidden code
5.4 ms
random_vect = rand(10)
👀 Reading hidden code
22.6 μs
👀 Reading hidden code
21.3 μs

Got it!

Great! 🎉

👀 Reading hidden code
21.6 ms

Hint

You can find out more about any function (like rand) by creating a new cell and typing:

?rand

Once the Live Docs are open, you can select any code to learn more about it. It might be useful to leave it open all the time, and get documentation while you type code.

👀 Reading hidden code
49.1 ms

👉 Make a function mean using a for loop, which computes the mean/average of a vector of numbers.

👀 Reading hidden code
284 μs
mean (generic function with 1 method)
function mean(x)
return sum(x) / length(x)
end
👀 Reading hidden code
537 μs
2.0
mean([1, 2, 3])
👀 Reading hidden code
19.7 μs

Got it!

Awesome!

👀 Reading hidden code
124 μs

👉 Define m to be the mean of random_vect.

👀 Reading hidden code
232 μs
0.5645743971538768
m = mean(random_vect)
👀 Reading hidden code
21.7 μs

Got it!

Great!

👀 Reading hidden code
125 μs

👉 Write a function demean, which takes a vector x and subtracts the mean from each value in x.

👀 Reading hidden code
232 μs
demean (generic function with 1 method)
function demean(x)
return x .- mean(x)
end
👀 Reading hidden code
568 μs

Let's check that the mean of the demean(random_vect) is 0:

Due to floating-point round-off error it may not be exactly 0.

👀 Reading hidden code
8.5 ms
👀 Reading hidden code
2.7 ms
-1.1102230246251566e-17
mean(demean(copy_of_random_vect))
👀 Reading hidden code
16.4 μs
copy_of_random_vect = copy(random_vect); # in case demean modifies `x`
👀 Reading hidden code
15.6 μs

Exercise 1.2

👉 Generate a vector of 100 zeros. Change the center 20 elements to 1.

👀 Reading hidden code
316 μs
create_bar (generic function with 1 method)
function create_bar()
x = zeros(100)
x[40:59] .= 1
return x
end
👀 Reading hidden code
555 μs
👀 Reading hidden code
18.9 μs

Got it!

Great!

👀 Reading hidden code
223 μs

Exercise 1.3

👉 Write a function that turns a Vector of Vectors into a Matrix.

👀 Reading hidden code
278 μs
vecvec_to_matrix (generic function with 1 method)
function vecvec_to_matrix(vecvec)
return hcat(vecvec...)
end
👀 Reading hidden code
401 μs
2×2 Matrix{Int64}:
 1  3
 2  4
vecvec_to_matrix([[1,2], [3,4]])
👀 Reading hidden code
61.6 ms

Got it!

Yay ❤

👀 Reading hidden code
49.0 ms

👉 Write a function that turns a Matrix into aVector of Vectors .

👀 Reading hidden code
235 μs
matrix_to_vecvec (generic function with 1 method)
function matrix_to_vecvec(matrix)
return collect(eachcol(matrix))
end
👀 Reading hidden code
427 μs
matrix_to_vecvec([6 7; 8 9])
👀 Reading hidden code
28.4 μs

Got it!

Great!

👀 Reading hidden code
163 μs
colored_line (generic function with 2 methods)
👀 Reading hidden code
976 μs





👀 Reading hidden code
11.3 μs

Exercise 2 - Manipulating images

In this exercise we will get familiar with matrices (2D arrays) in Julia, by manipulating images. Recall that in Julia images are matrices of RGB color objects.

Let's load a picture of Philip again.

👀 Reading hidden code
424 μs
"/tmp/jl_O2QZn1"
philip_file = download("https://i.imgur.com/VGPeJ6s.jpg")
👀 Reading hidden code
275 ms
philip = let
original = Images.load(philip_file)
decimate(original, 8)
end
👀 Reading hidden code
766 ms

Hi there Philip

👀 Reading hidden code
235 μs

Exercise 2.1

👉 Write a function mean_colors that accepts an object called image. It should calculate the mean (average) amounts of red, green and blue in the image and return a tuple (r, g, b) of those means.

👀 Reading hidden code
337 μs
mean_colors (generic function with 1 method)
function mean_colors(image)
c = mean(image)
c.r, c.g, c.b
end
👀 Reading hidden code
643 μs
mean_colors(philip)
👀 Reading hidden code
84.9 μs

Got it!

Well done!

👀 Reading hidden code
66.8 ms

Exercise 2.2

👉 Look up the documentation on the floor function. Use it to write a function quantize(x::Number) that takes in a value x (which you can assume is between 0 and 1) and "quantizes" it into bins of width 0.1. For example, check that 0.267 gets mapped to 0.2.

👀 Reading hidden code
6.8 ms
quantize (generic function with 3 methods)
begin
function quantize(x::Number)
return floor(10x) / 10
end
function quantize(color::AbstractRGB)
return RGB(quantize(color.r), quantize(color.g), quantize(color.b))
end
function quantize(image::AbstractMatrix)
return quantize.(image)
end
end
👀 Reading hidden code
1.2 ms
quantize(0.267), quantize(0.91)
👀 Reading hidden code
17.6 μs

Got it!

Well done!

👀 Reading hidden code
5.4 ms

Exercise 2.3

👉 Write the second method of the function quantize, i.e. a new version of the function with the same name. This method will accept a color object called color, of the type AbstractRGB.

Write the function in the same cell as quantize(x::Number) from the last exercise. 👆

Here, ::AbstractRGB is a type annotation. This ensures that this version of the function will be chosen when passing in an object whose type is a subtype of the AbstractRGB abstract type. For example, both the RGB and RGBX types satisfy this.

The method you write should return a new RGB object, in which each component (r, g and b) are quantized.

👀 Reading hidden code
821 μs

Exercise 2.4

👉 Write a method quantize(image::AbstractMatrix) that quantizes an image by quantizing each pixel in the image. (You may assume that the matrix is a matrix of color objects.)

Write the function in the same cell as quantize(x::Number) from the last exercise. 👆

👀 Reading hidden code
409 μs

Let's apply your method!

👀 Reading hidden code
201 μs
quantize(philip)
👀 Reading hidden code
2.1 ms

Exercise 2.5

👉 Write a function invert that inverts a color, i.e. sends (r,g,b) to (1r,1g,1b).

👀 Reading hidden code
268 μs
invert (generic function with 1 method)
function invert(color::AbstractRGB)
return RGB(1-color.r, 1-color.g, 1-color.b)
end
👀 Reading hidden code
570 μs

Let's invert some colors:

👀 Reading hidden code
183 μs
👀 Reading hidden code
13.9 μs
invert(black)
👀 Reading hidden code
12.8 μs
👀 Reading hidden code
14.8 μs
invert(red)
👀 Reading hidden code
11.2 μs

Can you invert the picture of Philip?

👀 Reading hidden code
183 μs
philip_inverted = invert.(philip)
👀 Reading hidden code
69.4 ms

Exercise 2.6

👉 Write a function noisify(x::Number, s) to add randomness of intensity s to a value x, i.e. to add a random value between s and +s to x. If the result falls outside the range (0,1) you should "clamp" it to that range. (Note that Julia has a clamp function, but you should write your own function myclamp(x).)

👀 Reading hidden code
304 μs
noisify (generic function with 3 methods)
begin
function noisify(x::Number, s)
return x + s*(rand()*2 - 1)
end
function noisify(color::AbstractRGB, s)
return RGB(noisify(color.r, s), noisify(color.g, s), noisify(color.b, s))
end
function noisify(image::AbstractMatrix, s)
return noisify.(image, [s])
end
end
👀 Reading hidden code
1.2 ms

Hint

The rand function generates (uniform) random floating-point numbers between 0 and 1.

👀 Reading hidden code
339 μs

👉 Write the second method noisify(c::AbstractRGB, s) to add random noise of intensity s to each of the (r,g,b) values in a colour.

Write the function in the same cell as noisify(x::Number) from the last exercise. 👆

👀 Reading hidden code
301 μs
0.0
👀 Reading hidden code
279 ms
noisify(red, color_noise)
👀 Reading hidden code
18.1 μs
👀 Reading hidden code
59.8 μs

👉 Write the third method noisify(image::AbstractMatrix, s) to noisify each pixel of an image.

Write the function in the same cell as noisify(x::Number) from the last exercise. 👆

👀 Reading hidden code
295 μs
0.0
@bind philip_noise Slider(0:0.01:8, show_value=true)
👀 Reading hidden code
882 μs
noisify(philip, philip_noise)
👀 Reading hidden code
2.7 ms
👀 Reading hidden code
60.0 μs

👉 For which noise intensity does it become unrecognisable?

You may need noise intensities larger than 1. Why?

👀 Reading hidden code
223 μs

The image is unrecognisable with intensity 3

The information density is lower than the pixel density - you can visually combine blurry neighbouring pixels to still form an image.

answer_about_noise_intensity = md"""
The image is unrecognisable with intensity 3

The information density is lower than the pixel density - you can visually combine blurry neighbouring pixels to still form an image.
"""
👀 Reading hidden code
316 μs
👀 Reading hidden code
64.5 μs
begin
Pkg.add("PlutoUI")
using PlutoUI
end
👀 Reading hidden code
❔
   Resolving package versions...
    Updating `/tmp/jl_PBYz5h/Project.toml`
  [7f904dfe] + PlutoUI v0.7.23
    Updating `/tmp/jl_PBYz5h/Manifest.toml`
  [6e696c72] + AbstractPlutoDingetjes v1.3.2
  [47d2ed2b] + Hyperscript v0.0.4
  [ac1192a8] + HypertextLiteral v0.9.5
  [b5f81e59] + IOCapture v0.2.5
  [682c06a0] + JSON v0.21.4
  [69de0a69] + Parsers v2.8.1
  [7f904dfe] + PlutoUI v0.7.23
  [410a4b4d] + Tricks v0.1.10
4.1 s
decimate (generic function with 2 methods)
👀 Reading hidden code
1.4 ms





👀 Reading hidden code
8.7 μs

Exercise 3 - Convolutions

As we have seen in the videos, we can produce cool effects using the mathematical technique of convolutions. We input one image M and get a new image M back.

Conceptually we think of M as a matrix. In practice, in Julia it will be a Matrix of color objects, and we may need to take that into account. Ideally, however, we should write a generic function that will work for any type of data contained in the matrix.

A convolution works on a small window of an image, i.e. a region centered around a given point (i,j). We will suppose that the window is a square region with odd side length 2+1, running from ,,0,,.

The result of the convolution over a given window, centred at the point (i,j) is a single number; this number is the value that we will use for Mi,j. (Note that neighbouring windows overlap.)

To get started let's restrict ourselves to convolutions in 1D. So a window is just a 1D region from to .

👀 Reading hidden code
780 μs

Let's create a vector v of random numbers of length n=100.

👀 Reading hidden code
214 μs
100
n = 100
👀 Reading hidden code
13.7 μs
v = rand(n)
👀 Reading hidden code
17.4 μs

Feel free to experiment with different values!

👀 Reading hidden code
277 μs

Exercise 3.1

You've seen some colored lines in this notebook to visualize arrays. Can you make another one?

👉 Try plotting our vector v using colored_line(v).

👀 Reading hidden code
329 μs
colored_line(v)
👀 Reading hidden code
22.6 μs

Try changing n and v around. Notice that you can run the cell v = rand(n) again to regenerate new random values.

👀 Reading hidden code
202 μs

Exercise 3.2

We need to decide how to handle the boundary conditions, i.e. what happens if we try to access a position in the vector v beyond 1:n. The simplest solution is to assume that vi is 0 outside the original vector; however, this may lead to strange boundary effects.

A better solution is to use the closest value that is inside the vector. Effectively we are extending the vector and copying the extreme values into the extended positions. (Indeed, this is one way we could implement this; these extra positions are called ghost cells.)

👉 Write a function extend(v, i) that checks whether the position i is inside 1:n. If so, return the ith component of v; otherwise, return the nearest end value.

👀 Reading hidden code
542 μs
extend (generic function with 1 method)
function extend(v, i)
if i < 1
v[1]
elseif i > length(v)
v[end]
else
v[i]
end
end
👀 Reading hidden code
658 μs

Some test cases:

👀 Reading hidden code
210 μs
0.03727101242784581
extend(v, 1)
👀 Reading hidden code
16.6 μs
0.03727101242784581
extend(v, -8)
👀 Reading hidden code
13.0 μs
0.5543432417321379
extend(v, n + 10)
👀 Reading hidden code
15.0 μs

Extended with 0:

👀 Reading hidden code
203 μs
👀 Reading hidden code
42.1 ms

Extended with your extend:

👀 Reading hidden code
180 μs
👀 Reading hidden code
43.2 μs

Got it!

Good job!

👀 Reading hidden code
122 μs

Exercise 3.3

👉 Write a function blur_1D(v, l) that blurs a vector v with a window of length l by averaging the elements within a window from to . This is called a box blur.

👀 Reading hidden code
303 μs
blur_1D (generic function with 1 method)
function blur_1D(v, l)
return map(eachindex(v)) do i
mean([extend(v, i+j) for j in -l:l])
end
end
👀 Reading hidden code
1.6 ms
👀 Reading hidden code
78.0 ms

Exercise 3.4

👉 Apply the box blur to your vector v. Show the original and the new vector by creating two cells that call colored_line. Make the parameter interactive.

👀 Reading hidden code
242 μs
0
👀 Reading hidden code
49.3 ms
colored_line(blur_1D(v, l))
👀 Reading hidden code
29.3 μs

Hint

Have a look at Exercise 2 to see an example of adding interactivity with a slider. You can read the Interactivity and the PlutoUI sample notebooks (right click -> Open in new tab) to learn more.

👀 Reading hidden code
409 μs

Exercise 3.5

The box blur is a simple example of a convolution, i.e. a linear function of a window around each point, given by

vi=nvinkn,

where k is a vector called a kernel.

Again, we need to take care about what happens if vin falls off the end of the vector.

👉 Write a function convolve_vector(v, k) that performs this convolution. You need to think of the vector k as being centred on the position i. So n in the above formula runs between and , where 2+1 is the length of the vector k. You will need to do the necessary manipulation of indices.

👀 Reading hidden code
999 μs
convolve_vector (generic function with 1 method)
function convolve_vector(v, k)
l = (length(k) - 1) ÷ 2
return map(eachindex(v)) do i
sum([extend(v, i - j) * k[j + 1 + l] for j in -l:l])
end
end
👀 Reading hidden code
1.8 ms

Hint

l = (length(k) - 1) ÷ 2

👀 Reading hidden code
259 μs
👀 Reading hidden code
27.5 μs
test_convolution = let
v = [1, 10, 100, 1000, 10000]
k = [1, 1, 0]
convolve_vector(v, k)
end
👀 Reading hidden code
37.2 μs

Edit the cell above, or create a new cell with your own test cases!

👀 Reading hidden code
272 μs

Got it!

Let's move on to the next section.

👀 Reading hidden code
218 μs

Exercise 3.6

👉 Write a function gaussian_kernel.

👀 Reading hidden code
252 μs
pascal (generic function with 1 method)
function pascal(n)
if n <= 1
[1.0]
else
prev = pascal(n - 1)
0.5 * ([prev..., 0] .+ [0, prev...])
end
end
👀 Reading hidden code
876 μs
gaussian_kernel (generic function with 1 method)
function gaussian_kernel(n)
pascal(2n+1)
end
👀 Reading hidden code
468 μs
gaussian_kernel.(0:5)
👀 Reading hidden code
609 ms

Let's test your kernel function!

👀 Reading hidden code
180 μs
@bind gaussian_kernel_size_1D Slider(0:6)
👀 Reading hidden code
8.2 ms
👀 Reading hidden code
15.5 μs
test_gauss_1D_a = let
v = random_vect
k = gaussian_kernel(gaussian_kernel_size_1D)
if k !== missing
convolve_vector(v, k)
end
end
👀 Reading hidden code
66.9 ms
👀 Reading hidden code
14.8 μs
test_gauss_1D_b = let
v = create_bar()
k = gaussian_kernel(gaussian_kernel_size_1D)
if k !== missing
convolve_vector(v, k)
end
end
👀 Reading hidden code
54.3 μs





👀 Reading hidden code
9.3 μs

Exercise 4 - Convolutions of images

Now let's move to 2D images. The convolution is then given by a kernel matrix K:

Mi,j=k,lMik,jlKk,l,

where the sum is over the possible values of k and l in the window. Again we think of the window as being centered at (i,j).

👀 Reading hidden code
486 μs

Exercise 4.1

👉 Write a function extend_mat that takes a matrix M and indices i and j, and returns the closest element of the matrix.

👀 Reading hidden code
254 μs
extend_mat (generic function with 1 method)
function extend_mat(M::AbstractMatrix, i, j)
if i < 1
extend_mat(M, 1, j)
elseif i > size(M, 1)
extend_mat(M, size(M,1), j)
else
if j < 1
extend_mat(M, i, 1)
elseif j > size(M, 2)
extend_mat(M, i, size(M,2))
else
M[i,j]
end
end
end
👀 Reading hidden code
912 μs

Hint

num_rows, num_columns = size(M)

👀 Reading hidden code
281 μs

Let's test it!

👀 Reading hidden code
216 μs
small_image = Gray.(rand(5,5))
👀 Reading hidden code
78.2 ms

Extended with 0:

👀 Reading hidden code
183 μs
👀 Reading hidden code
161 ms

Extended with your extend:

👀 Reading hidden code
182 μs
👀 Reading hidden code
34.4 ms

Got it!

Good job!

👀 Reading hidden code
210 μs
👀 Reading hidden code
36.3 ms

Exercise 4.2

👉 Implement a function convolve_image(M, K).

👀 Reading hidden code
228 μs
convolve_image (generic function with 1 method)
function convolve_image(M::AbstractMatrix, K::AbstractMatrix)
l = (size(K,1) - 1) ÷ 2
map(CartesianIndices(M)) do i
sum(CartesianIndices(K)) do a
offset = a - CartesianIndex(-l-1, -l-1)
extend_mat(M, (i - offset).I...) * K[a]
end
end
end
👀 Reading hidden code
2.1 ms

Hint

num_rows, num_columns = size(K)

👀 Reading hidden code
242 μs

Let's test it out! 🎃

👀 Reading hidden code
218 μs
👀 Reading hidden code
88.0 ms
3×3 Matrix{Float64}:
 0.0  0.0  0.0
 0.5  0.0  0.5
 0.0  0.0  0.0
K_test = [
0 0 0
1/2 0 1/2
0 0 0
]
👀 Reading hidden code
28.7 ms
convolve_image(test_image_with_border, K_test)
👀 Reading hidden code
58.6 ms

Edit K_test to create your own test case!

👀 Reading hidden code
268 μs
convolve_image(philip, K_test)
👀 Reading hidden code
11.0 ms

You can create all sorts of effects by choosing the kernel in a smart way. Today, we will implement two special kernels, to produce a Gaussian blur and a Sobel edge detect filter.

Make sure that you have watched the lecture about convolutions!

👀 Reading hidden code
437 μs

Exercise 4.3

👉 Apply a Gaussian blur to an image.

👀 Reading hidden code
332 μs

Hint

Can we just copy the 1D code? What is different in 2D?

👀 Reading hidden code
246 μs
with_gaussian_blur (generic function with 1 method)
function with_gaussian_blur(image; sigma=3, l=5)
gauss(x,y; sigma=3) = (1/(2*pi*sigma^2))exp(-(x^2 + y^2)/(2*sigma^2))
K_gauss = [gauss(xy...) for xy in Iterators.product(-l:l,-l:l)]
convolve_image(image, K_gauss ./ sum(K_gauss))
end
👀 Reading hidden code
4.7 ms

Let's make it interactive. 💫

👀 Reading hidden code
234 μs
Enable webcam
👀 Reading hidden code
52.4 ms
Error message

Another cell defining gauss_camera_image contains errors.

with_gaussian_blur(gauss_camera_image)
👀 Reading hidden code
---
Error message

MethodError: no method matching getindex(::Missing, ::String)

Stack trace

Here is what happened, the most recent locations are first:

  1. process_raw_camera_data(raw_camera_data::Missing)
    	# So to get the red values for each pixel, we take every 4th value, starting at 	# the 1st:	reds_flat = UInt8.(raw_camera_data["data"][1:4:end])	greens_flat = UInt8.(raw_camera_data["data"][2:4:end])	blues_flat = UInt8.(raw_camera_data["data"][3:4:end])
  2. Show more...
gauss_camera_image = process_raw_camera_data(gauss_raw_camera_data);
👀 Reading hidden code
---

Exercise 4.4

👉 Create a Sobel edge detection filter.

👀 Reading hidden code
288 μs
with_sobel_edge_detect (generic function with 1 method)
function with_sobel_edge_detect(image)
K_sobol = [
1 0 -1
2 0 -2
1 0 -1
]
x = convolve_image(image, K_sobol)
y = convolve_image(image, K_sobol')
return x .* x .+ y .* y
end
👀 Reading hidden code
808 μs
Enable webcam
👀 Reading hidden code
815 μs
Error message

Another cell defining sobel_camera_image contains errors.

with_sobel_edge_detect(sobel_camera_image)
👀 Reading hidden code
---
Error message

MethodError: no method matching getindex(::Missing, ::String)

Stack trace

Here is what happened, the most recent locations are first:

  1. process_raw_camera_data(raw_camera_data::Missing)
    	# So to get the red values for each pixel, we take every 4th value, starting at 	# the 1st:	reds_flat = UInt8.(raw_camera_data["data"][1:4:end])	greens_flat = UInt8.(raw_camera_data["data"][2:4:end])	blues_flat = UInt8.(raw_camera_data["data"][3:4:end])
  2. Show more...
Silly computer!
sobel_camera_image = Gray.(process_raw_camera_data(sobel_raw_camera_data));
👀 Reading hidden code
---





👀 Reading hidden code
8.8 μs

Exercise 5 - Lecture transcript

(MIT students only)

Please see the Piazza post for transcript document here.

👉 Which lines of the transcripts did you edit?

👀 Reading hidden code
525 μs

Lecture 1, lines 10-50 (for example)

lines_i_edited = md"""
Lecture 1, lines 10-50 (_for example_)
"""
👀 Reading hidden code
396 μs





👀 Reading hidden code
10.0 μs
👀 Reading hidden code
306 μs





👀 Reading hidden code
9.5 μs
hint (generic function with 1 method)
👀 Reading hidden code
511 μs
almost (generic function with 1 method)
👀 Reading hidden code
549 μs
still_missing (generic function with 2 methods)
👀 Reading hidden code
911 μs
keep_working (generic function with 2 methods)
👀 Reading hidden code
921 μs
👀 Reading hidden code
11.0 ms
correct (generic function with 2 methods)
👀 Reading hidden code
820 μs
not_defined (generic function with 1 method)
👀 Reading hidden code
1.1 ms
👀 Reading hidden code
32.7 μs
camera_input (generic function with 1 method)
👀 Reading hidden code
1.4 ms
process_raw_camera_data (generic function with 1 method)
👀 Reading hidden code
1.4 ms